﻿using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace UnitTesting.Fixturing
{
	class ArgumentSpecifier : IArgumentSpecifier, IArgumentProvider
	{
		readonly List<IArgument> _specifiedArguments = new List<IArgument>();
		List<IArgument> _tempArguments;
		ParameterInfo[] _parameters;

		public void WithArgument(IArgument argument)
		{
			Ensure.IsNotNull(() => argument);
			_specifiedArguments.Add(argument);
		}

		public object[] GetArguments(params ParameterInfo[] parameters)
		{
			_parameters = Ensure.IsNotNull(() => parameters);
			_tempArguments = CopyFromArguments();
			var result = GetArgumentValues();
			EnsureAllTheArgumentsAreApplied();
			return result;
		}

		List<IArgument> CopyFromArguments()
		{
			return new List<IArgument>(_specifiedArguments);
		}

		object[] GetArgumentValues()
		{
			return _parameters.Select(GetArgumentValue).ToArray();
		}

		object GetArgumentValue(ParameterInfo parameter)
		{
			var argument = GetArgumentOrNull(parameter);
			if (argument != null)
			{
				_tempArguments.Remove(argument);
				return argument.Value;
			}
			return NotResolved.Value;
		}

		IArgument GetArgumentOrNull(ParameterInfo parameter)
		{
			return _tempArguments.FirstOrDefault(arg => arg.Equals(parameter));
		}

		void EnsureAllTheArgumentsAreApplied()
		{
			if (_tempArguments.Any())
			{
				throw new NotMatchedArgumentException(_specifiedArguments);
			}
		}
	}
}